/*******************************************************************************
 * AudioLibView.cpp
 *
 * Copyright (c) 2013, Varuna L Amachi. All rights reserved.
 *
 * This program/library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This program/library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301  USA
 ******************************************************************************/

#include <QFileDialog>
#include <QDesktopServices>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QMessageBox>
#include <QIcon>
#include <QMenu>
#include <QContextMenuEvent>
#include <QDragEnterEvent>
#include <QDropEvent>
#include <QHeaderView>
#include <QPixmapCache>
#include <core/manager/T.h>
#include <ui/widgets/RatingWidget.h>


#include "AudioLibView.h"
#include "../common/RatingDelegate.h"

using namespace GreenChilli;
using namespace AudioLib;

QString AudioLibView::COMPONENT_ID = "GreeChilli::Std::AudioLibView";
QString AudioLibView::COMPONENT_NAME = "Tracks";


AudioLibView::AudioLibView(QWidget *parent) :
    ChilliComponent( parent ),
    m_view( new QTreeView( this )),
    m_toolBar( new QToolBar( this )),
    m_sortFilter( new TrackFilter( this )),
    m_progress( new QProgressBar( this )),
    m_status( new QLabel( this )),
    m_filterEdit( new Tanyatu::Gui::SearchBox( this )),
    m_loader( new Tanyatu::Worker::AudioLibraryLoader( this ))
{
    setupActions();
    setupUi();
    setupConnections();
    setAcceptDrops( true );
}


void AudioLibView::contextMenuEvent(QContextMenuEvent *event)
{
    QMenu menu( this );
    menu.addAction( m_actPlayTracks );
    menu.addAction( m_actPlayFromArtist );
    menu.addAction( m_actPlayFromAlbum );
    menu.addSeparator();
    menu.addAction( m_actAddToPlaylist );
    menu.addAction( m_actAddFromArtist );
    menu.addAction( m_actAddFromAlbum );
    menu.addSeparator();
    menu.addAction( m_actAddDir );
    menu.addAction( m_actAddFiles );
    menu.addSeparator();
    menu.addAction( m_actRemove );
    menu.addAction( m_actClear );
    menu.exec( event->globalPos() );
}


void AudioLibView::dragEnterEvent(QDragEnterEvent *event)
{
    if( event->mimeData()->hasFormat( "text/uri-list" )) {
        event->acceptProposedAction();
    }
}


void AudioLibView::dropEvent(QDropEvent *event)
{
    QList< QUrl > *urls = new QList< QUrl >();
    QList< Tanyatu::Model::StoredAudio *> *loadedList =
            new QList< Tanyatu::Model::StoredAudio *>();

    urls->append( event->mimeData()->urls() );
    if( ! m_loader->load( urls, loadedList )) {
        delete urls;
    }
}


void AudioLibView::setupUi()
{
    QHBoxLayout *upperLayout = new QHBoxLayout();
    upperLayout->addWidget( new QLabel( tr( "Filter" ), this ));
    upperLayout->addWidget( m_filterEdit );
    upperLayout->addWidget( m_toolBar );

    QHBoxLayout *bottomLayout = new QHBoxLayout();
    bottomLayout->addWidget( m_status );
    bottomLayout->addWidget( m_progress );


    QVBoxLayout *mainLayout = new QVBoxLayout();
    mainLayout->addLayout( upperLayout );
    mainLayout->addWidget( m_view );
    mainLayout->addLayout( bottomLayout );
//    mainLayout->setContentsMargins( 0, 0, 0, 0 );
    this->setContentsMargins( 0, 0, 0, 0 );
    this->setLayout( mainLayout );

    m_progress->hide();
    m_status->hide();
    m_sortFilter = new TrackFilter( this );
    m_sortFilter->setSourceModel( AUDIO_LIB() );
    m_sortFilter->setDynamicSortFilter( true );
    Common::RatingDelegate *delegate = new Common::RatingDelegate( 4, this );
    m_sortFilter->sort( 1 );

    m_view->setModel( m_sortFilter );
    m_view->setItemDelegate( delegate );
    m_view->setSortingEnabled( true );
    m_view->setWordWrap( false );
    m_view->setAutoScroll( true );
    m_view->header()->setResizeMode( 1, QHeaderView::Stretch );
    m_view->header()->setResizeMode( 2, QHeaderView::Stretch );
    m_view->header()->setResizeMode( 3, QHeaderView::Stretch );
    m_view->setColumnWidth( 4, 16 );
    m_view->header()->setStretchLastSection( false );
    m_view->hideColumn( 0 );
    m_view->setSelectionMode( QAbstractItemView::ExtendedSelection );
    m_view->setSelectionBehavior( QAbstractItemView::SelectRows );
    m_view->setFocusPolicy( Qt::WheelFocus );
    m_view->setEditTriggers(QAbstractItemView::CurrentChanged);
    m_view->setAlternatingRowColors(true);
    m_view->setFont( QFont( m_view->font().defaultFamily(), 8 ));

}


void AudioLibView::setupActions()
{
    m_actRemove   = new QAction( QIcon( ":/images/remove" ),
                               tr( "Remove From library" ),
                               this );
    m_actClear    = new QAction( QIcon ( ":/images/clear"),
                               tr( "Clear Library" ),
                               this );
    m_actAddDir   = new QAction( QIcon( ":/images/addfolder" ),
                               tr( "Add Directory" ),
                               this );
    m_actAddFiles = new QAction( QIcon( ":/images/addfiles" ),
                               tr( "Add Files" ),
                               this );

    m_actPlayTracks = new QAction( tr( "Play" ), this );
    m_actPlayFromArtist = new QAction( tr( "Play from this artist" ), this );
    m_actPlayFromAlbum = new QAction( tr( "Play this album" ), this);
    m_actAddToPlaylist  = new QAction( tr( "Add to Playlist" ), this );
    m_actAddFromArtist = new QAction( tr( "Add from Artist" ), this );
    m_actAddFromAlbum  = new QAction( tr( "Add from Album" ), this );

    m_toolBar->addAction( m_actAddDir );
    m_toolBar->addAction( m_actAddFiles );
    m_toolBar->addAction( m_actRemove );
    m_toolBar->addAction( m_actClear );
}


void AudioLibView::setupConnections()
{
    connect( m_actPlayTracks, SIGNAL( triggered() ),
             this, SLOT( playTracks() ));
    connect( m_actPlayFromArtist, SIGNAL( triggered() ),
             this, SLOT( playArtist() ));
    connect( m_actPlayFromAlbum, SIGNAL( triggered() ),
             this, SLOT( playAlbum() ));
    connect( m_actAddDir, SIGNAL( triggered() ),
             this, SLOT( addDirectory() ));
    connect( m_actAddFiles, SIGNAL( triggered() ),
             this, SLOT( addFiles() ) );
    connect( m_actAddToPlaylist, SIGNAL( triggered() ),
             this, SLOT( onTracksEnqueue() ));
    connect( m_actAddFromArtist, SIGNAL( triggered() ),
             this, SLOT( onArtistEnqueue() ));
    connect( m_actAddFromAlbum, SIGNAL( triggered() ),
             this, SLOT( onAlbumEnqueue() ));
    connect( m_actRemove, SIGNAL( triggered() ),
             this, SLOT( removeFromLibrary() ));
    connect( m_actClear, SIGNAL( triggered() ),
             this, SLOT( clearLibrary() ));
    connect( m_view, SIGNAL( activated( QModelIndex )),
             this, SLOT( playTracks() ));
    connect( m_filterEdit, SIGNAL( textChanged( QString )),
             m_sortFilter, SLOT( setExpression(QString) ));
    connect( m_loader, SIGNAL( loadingStarted( int )),
             this, SLOT( onLoadingStarted(int) ));
    connect( m_loader, SIGNAL( loadedItem( Tanyatu::Model::StoredAudio* )),
             this, SLOT( onAudioTrackLoaded( Tanyatu::Model::StoredAudio* )));
    connect( m_loader, SIGNAL( loadProgress( int, int, QString )),
             this, SLOT( showLoadProgress( int, int )));
    connect( m_loader, SIGNAL(
                 loadingFinished(
                     QList< QUrl >*, QList< Tanyatu::Model::StoredAudio *> *)),
             this, SLOT(
                 onLoadingFinished(
                     QList<QUrl>*, QList<Tanyatu::Model::StoredAudio *> *)));
    connect( AUDIO_LIB(), SIGNAL( ratingChanged( Tanyatu::Model::StoredAudio*)),
             this, SLOT( onRatingChanged() ));
    connect( AUDIO_LIB(), SIGNAL( itemChanged( Tanyatu::Model::StoredAudio* )),
              this, SLOT( onTrackInfoUpdated( Tanyatu::Model::StoredAudio* )));
    connect( AUDIO_MANAGER(),
             SIGNAL( artistAdded( Tanyatu::Model::ArtistInfo * )),
             this,
             SLOT( onArtistAdded( Tanyatu::Model::ArtistInfo * )));
    connect( AUDIO_MANAGER(),
             SIGNAL( albumAdded( Tanyatu::Model::AlbumInfo * )),
             this,
             SLOT( onAlbumAdded( Tanyatu::Model::AlbumInfo * )));
    connect( m_filterEdit, SIGNAL( returnPressed() ),
             this, SLOT( onFilterBoxCommitted() ));
}


void AudioLibView::addDirectory()
{
    QString dirName = QFileDialog::getExistingDirectory( this,
                                tr( "Add Folder" ),
                                QDesktopServices::storageLocation(
                                            QDesktopServices::MusicLocation ));
    if( ! dirName.isEmpty() ) {
        QList< QUrl > *urls = new QList< QUrl >();
        urls->append( QUrl::fromLocalFile( dirName ));
        QList< Tanyatu::Model::StoredAudio *> *loadedList =
                new QList< Tanyatu::Model::StoredAudio *>();
        if( ! m_loader->load( urls, loadedList )) {
            delete urls;
            delete loadedList;
        }
    }
}

void AudioLibView::addFiles()
{
    QStringList files = QFileDialog::getOpenFileNames( this,
                                tr( "Select Music Files" ),
                                QDesktopServices::storageLocation(
                                            QDesktopServices::MusicLocation ));
    if( ! files.empty() ) {
        QList<QUrl> *urls = new QList< QUrl >();
        foreach( QString fileName, files ) {
            urls->append( QUrl::fromLocalFile( fileName ));
        }
        QList< Tanyatu::Model::StoredAudio *> *loadedList =
                new QList< Tanyatu::Model::StoredAudio *>();
        if( ! m_loader->load( urls, loadedList )) {
            delete urls;
            delete loadedList;
        }
    }
}


void AudioLibView::onArtistEnqueue()
{
    QModelIndexList indexList = m_view->selectionModel()->selectedRows( 0 );
    if( ! indexList.isEmpty() ) {
        QString trackId;
        QSet< QString > seleactedArtists;
        QList< QString > trackIds;
        foreach( const QModelIndex &index, indexList ) {
            trackId = index.data().toString();
            Tanyatu::Model::StoredAudio *track = AUDIO_LIB()->item( trackId );
            if( ! seleactedArtists.contains( track->artist() ))
            {
                seleactedArtists.insert( track->artist() );
                AUDIO_MANAGER()->tracksFromArtist( track->artist(), trackIds );
            }
        }
        QList< Tanyatu::Model::MediaItem *> itemsOut;
        AUDIO_LIB()->items( trackIds, itemsOut );
        PLAYLIST()->addItems( itemsOut );
    }
}


void AudioLibView::onAlbumEnqueue()
{
    QModelIndexList indexList = m_view->selectionModel()->selectedRows( 0 );
    if( ! indexList.isEmpty() ) {
        QString trackId;
        QSet< QString > seleactedAlbums;
        QList< QString > trackIds;
        foreach( const QModelIndex &index, indexList ) {
            trackId = index.data().toString();
            Tanyatu::Model::StoredAudio *track = AUDIO_LIB()->item( trackId );
            QString albumId = Tanyatu::Model::AlbumInfo::getAlbumIdFrom(
                        track->artist(), track->album() );
            if( ! seleactedAlbums.contains( albumId ))
            {
                seleactedAlbums.insert( albumId );
                AUDIO_MANAGER()->tracksFromAlbum( albumId,
                                                  trackIds );
            }
        }
        QList< Tanyatu::Model::MediaItem *> itemsOut;
        AUDIO_LIB()->items( trackIds, itemsOut );
        PLAYLIST()->addItems( itemsOut );
    }
}


void AudioLibView::onTracksEnqueue()
{
    QModelIndexList indices = m_view->selectionModel()->selectedRows(0);
    if( ! indices.empty() ) {
        foreach( QModelIndex index, indices ) {
            QString trackId = index.data().toString();
            Tanyatu::Model::StoredAudio *track = AUDIO_LIB()->item( trackId );
            PLAYLIST()->addItem( track, false );
        }
    }
}


void AudioLibView::playArtist()
{
    int playIndex = PLAYLIST()->numberOfItems();
    onArtistEnqueue();
    if( playIndex != PLAYLIST()->numberOfItems() ) {
        PLAYLIST()->selectIndex( playIndex );
    }
}


void AudioLibView::playAlbum()
{
    int playIndex = PLAYLIST()->numberOfItems();
    onAlbumEnqueue();
    if( playIndex != PLAYLIST()->numberOfItems() ) {
        PLAYLIST()->selectIndex( playIndex );
    }
}


void AudioLibView::playTracks()
{
    int playIndex = PLAYLIST()->numberOfItems();
    onTracksEnqueue();
    if( playIndex != PLAYLIST()->numberOfItems() ) {
        PLAYLIST()->selectIndex( playIndex );
    }
}


void AudioLibView::removeFromLibrary()
{
    if( DBCMD_HANDLER()->hasPendingTasks() ) {
        QMessageBox::warning( this,
                              tr( "Unable process request" ),
                              tr( "GreenChilli is busy processing other "
                                  "library operations and hence cannot "
                                  "process any removal requests. "
                                  " Please try again later." ));
        return;
    }
    QMessageBox::StandardButton button = QMessageBox::question(
                this,
                tr( "Confirm Audio Tracks Deletion" ),
                tr( "This operation will remove tracks from the library "
                    "(The files themselves will not be deleted.) do you want "
                    "to continue?"),
                    QMessageBox::Cancel | QMessageBox::Ok );
    if( button & QMessageBox::Ok )  {
        QModelIndexList indices = m_view->selectionModel()->selectedRows(0);
        QList< QString > *remIds = new QList< QString >();
        foreach( QModelIndex index, indices ) {
            remIds->append( index.data().toString() );
        }
        AUDIO_LIB()->remove( *remIds );
        DBCMD_HANDLER()->addCmd(
                    DBCMD_CODE::Cmd_RemoveTracksFromLib,
                    reinterpret_cast< void *>( remIds ));
    }
}


void AudioLibView::clearLibrary()
{
    QMessageBox::StandardButton button = QMessageBox::question(
                this,
                tr( "Confirmation: Removal from library" ),
                tr( "This operation will clear all the content from the library"
                    "(The files themselves will not be deleted.) do you want "
                    "to continue?" ),
                    QMessageBox::Cancel | QMessageBox::Ok );
    if( button & QMessageBox::Ok ) {
        AUDIO_LIB()->deleteAll();
        DBCMD_HANDLER()->addCmd( DBCMD_CODE::Cmd_ClearAudioLib, 0 );
    }
}


void AudioLibView::onLoadingFinished(
        QList< QUrl > *inputUrls,
        QList< Tanyatu::Model::StoredAudio *> *loadedTracks )
{
    DBCMD_HANDLER()->addCmd( DBCMD_CODE::Cmd_AddTracksToLib,
                             reinterpret_cast< void *>( loadedTracks ));
    DBCMD_HANDLER()->addCmd( DBCMD_CODE::Cmd_AddStoredObjects,
                             reinterpret_cast< void *>( m_addedObjects ));
    delete inputUrls;
    m_progress->setVisible( false );
    clearStatus();
    m_view->repaint();
}

void AudioLibView::onRatingChanged()
{
    m_view->repaint();
}

void AudioLibView::onTrackInfoUpdated( Tanyatu::Model::StoredAudio *track )
{
    if( track ) {
        DBCMD_HANDLER()->addCmd( DBCMD_CODE::Cmd_UpdateTrackInfo,
                                  static_cast< void *>( track ));
    }
}


void AudioLibView::onLoadingStarted( int numFilesToLoad ) {
    m_progress->setVisible( true );
    setStatus( tr("Loading...") );
    m_addedObjects = new QList< Tanyatu::Model::StoredObject *>();
    m_progress->setMaximum( numFilesToLoad );
}


void AudioLibView::setStatus( QString text ) {
    m_status->setText( text );
    m_status->setVisible( true );
}


void AudioLibView::clearStatus() {
    m_status->hide();
}

void AudioLibView::showLoadProgress( int numFilesLoaded,
                                       int totalFilesToLoad )
{
    Q_UNUSED( totalFilesToLoad );
    m_progress->setValue( numFilesLoaded );
}

void AudioLibView::onAudioTrackLoaded( Tanyatu::Model::StoredAudio *track )
{
    AUDIO_LIB()->add( track );
    m_view->repaint();
}


const QIcon* AudioLibView::icon() const
{
    return 0;
}


QString AudioLibView::componentName() const
{
    return COMPONENT_NAME;
}


QString AudioLibView::componentId() const
{
    return COMPONENT_ID;
}


void AudioLibView::onArtistAdded( Tanyatu::Model::ArtistInfo *artist )
{
    if( artist ) {
        m_addedObjects->append( artist );
    }
}


void AudioLibView::onAlbumAdded( Tanyatu::Model::AlbumInfo *album )
{
    if( album ) {
        m_addedObjects->append( album );
    }
}

void AudioLibView::onFilterBoxCommitted()
{
    m_view->selectAll();
    playTracks();
}
